
# coding: utf-8
from hashlib import blake2b as hash_
import time
from reedsolo import RSCodec ,ReedSolomonError
from tqdm import tqdm
import base64
import wx   #wxpython
import wx.xrc
import wx.grid as grid
import os
import sqlite3
import logging
from pubsub import pub #pypubsub
from hash_gui import *
from inc import *


logging.basicConfig(level=logging.INFO)
logfilename = "blake2.log"
# BackupFileSize=1000
defaultConfig={"expirationDate":90
               ,'BackupFileSize':1000
               ,'sqlBlockSize':100
               ,'compression':0
               ,"eccLength":8
               }
def RelativePath(root,path):
    return path[len(root.rstrip("\\")):]

def getConfig(db,key=None):
    cur=db.cursor()
    if(key==None):
        return None
    else:
        try:
            sql=f"select val from config where key = '{key}';"
            res=cur.execute(sql)
        except BaseException as e:
            logging.error(f"get Config error {e}")
            return None
        else:
            val=res.fetchone()
            if(val==None):
                if('expirationDate' in defaultConfig.keys()):
                    return defaultConfig[key]
                else:
                    return None
            else:
                return val[0]
def setConfig(db,key,val):
    if(type(key)==type(str())):
        try:
            sql=f"replace into config('key','val') values('{key}','{val}');"
            db.execute(sql)
            db.commit()
        except BaseException as e:
            logging.error(f"set config error :{e}")
            return False
        else:
            return True
    else:
        try:
            for i in key.items():
                sql=sql=f"replace into config('key','val') values('{i[0]}','{i[1]}');"
                db.execute(sql)
            db.commit()
        except BaseException as e:
            logging.error(f"set config error :{e}")
            return False
        else:
            return True
def msg(content, title="文件校验", type=wx.ICON_INFORMATION):
    dlg = wx.MessageDialog(None, content, title, type)
    r = dlg.ShowModal()
    dlg.Destroy()
    return r

def trySql(db,sql):
    try:
        if(type(sql)==str):
            db.execute(sql)
        if(type(sql)==list):
            for s in sql:
                db.execute(s)
    except (sqlite3.Error, Exception) as e:
        logging.error("Database error: %s" % e)
        db.rollback()
        return False
    else:
        db.commit()
        return True
def createDB(root):
    flag_=True
    try:
        db=sqlite3.connect(f"{root}\\{logfilename}.db",check_same_thread=False)
        flag_=flag_ and trySql(db,'create table list (path varchar(256) NOT NULL primary key, hash varchar(2),  checks varchar(2))')
        flag_=flag_ and trySql(db,'create table dirs (path varchar(256) NOT NULL primary key, hash varchar(2),  checks varchar(2))')
        flag_=flag_ and trySql(db,"create table hash (path varchar(256) primary key, hash varchar(128))")
        flag_=flag_ and trySql(db,"create table checks (path varchar(256) primary key, hash varchar(128),timestamp INTEGER);")
        flag_=flag_ and trySql(db, "CREATE TABLE \"config\" (\"key\"  TEXT NOT NULL,\"val\"  TEXT,PRIMARY KEY (\"key\"));",)
        flag_=flag_ and trySql(db,"INSERT INTO \"config\" (\"key\", \"val\") VALUES (\'ver\', 1);")
        flag_=flag_ and trySql(db,"CREATE INDEX index_hash ON hash (hash);")
        db.commit()
        db.close()
    except:
        return False
    else:
        return flag_
def createView(root):
    flag_=True
    try:
        db=sqlite3.connect(f"{root}\\{logfilename}.db",check_same_thread=False)    
        flag_=flag_ and trySql(db,"DROP VIEW IF EXISTS \"repeat\";")
        flag_=flag_ and trySql(db,"CREATE VIEW \"main\".\"repeat\" AS SELECT hash.path,hash.hash FROM hash WHERE hash.hash IN (select hash.hash from hash GROUP BY hash.hash having count(hash.hash)>1) ORDER BY hash.hash DESC;")
        flag_=flag_ and trySql(db,"DROP VIEW IF EXISTS \"check\";")
        flag_=flag_ and trySql(db,"CREATE VIEW \"main\".\"check\" AS SELECT checks.path,hash.hash AS hash ,checks.hash AS checks ,checks.timestamp  FROM checks LEFT JOIN hash ON checks.path = hash.path WHERE checks.hash <> hash.hash OR checks.hash IS NULL OR hash.hash IS NULL;")
        flag_=flag_ and trySql(db,"DROP VIEW IF EXISTS \"hashlist\";")
        flag_=flag_ and trySql(db,"CREATE VIEW \"main\".\"hashlist\" AS SELECT list.path , hash.hash FROM list LEFT JOIN hash ON hash.path = list.path ;")
        flag_=flag_ and trySql(db,"DROP VIEW IF EXISTS \"checkslist\";")
        flag_=flag_ and trySql(db,"CREATE VIEW \"main\".\"checkslist\" AS SELECT list.path , checks.hash , checks.timestamp FROM list LEFT JOIN checks ON checks.path = list.path ORDER BY timestamp ASC;")
        flag_=flag_ and trySql(db,"DROP VIEW IF EXISTS \"alllist\";")
        flag_=flag_ and trySql(db,"CREATE VIEW \"alllist\" AS SELECT list.path, hash.hash, checks.hash AS checks, checks.timestamp FROM list LEFT JOIN hash ON list.path = hash.path LEFT JOIN checks ON checks.path = list.path ;")
        flag_=flag_ and trySql(db,"DROP VIEW IF EXISTS \"main\".\"lostfile\" ;")
        flag_=flag_ and trySql(db,"CREATE VIEW \"main\".\"lostfile\" AS SELECT hash.path, hash.hash FROM hash LEFT JOIN list ON list.path = hash.path WHERE list.hash is NULL ;")
        flag_=flag_ and trySql(db,"DROP VIEW IF EXISTS \"main\".\"newfile\";")
        flag_=flag_ and trySql(db,"CREATE VIEW \"main\".\"newfile\" AS SELECT list.path , hash.hash FROM list LEFT JOIN hash ON hash.path = list.path WHERE hash.hash IS NULL;")



    except:
        return False
    else:
        return flag_
def rscodefactory(cursor, row):
    b64=base64.b64encode(ecc.encode(bytes.fromhex(row[1])))
    return (b64.decode(),row[0])
def eccBase64En(row,ecclen=8):
    ecc=RSCodec(ecclen)
    try:
        en=f"{base64.b64encode(ecc.encode(bytes.fromhex(row[1]))).decode()}"
    except :
        logging.error(f"RScode编码错误！")
    else:
        return en
def eccBase64De(str,ecclen=8):
    ecc=RSCodec(ecclen)
    try:
        rsdecode=ecc.decode(base64.b64decode(str))
        if(rsdecode[2] != b''):
            logging.error(f"ECC 发现错误! :\n{str}\n{base64.b64encode(rsdecode[1]).decode()}")
            d3=False
        else:
            d3=True
    except ReedSolomonError :
        logging.error("ECC 纠错失败")
        return False
    except Exception as e:
        logging.error(f"RScode解码错误:{e}")
    else:
        d1=rsdecode[0].hex().upper()
        d2=base64.b64encode(rsdecode[1]).decode()
        return (d1,d2,d3)

if __name__ == "__main__":
    import easygui
    root = os.path.dirname(os.path.realpath(__file__))
    info = {"ThreadRun": False,
            "dircount": 0, "filecount": 0,
            "filelist": [], 'complete': 0,
            "path": "", 'root': None, 'check': False}
    
    BackupFileSize=5000
    ecc=RSCodec(32)#纠错码字长度，可纠正len/2个误读
    # dbpath=easygui.fileopenbox()
    # db = sqlite3.connect(dbpath, check_same_thread=False)
    db = sqlite3.connect("d:\\hubo1\\blake2.log.db", check_same_thread=False)
    setConfig(db,dc,None)
    print(getConfig(db,'expirationDate'))
